 1.zk实现分布式锁
   
   利用Zookeeper可以创建临时带序号节点的特性来实现一个分布式锁
   实现思路
   1).锁就是zk指定目录下序号最小的临时序列节点，多个系统的多个线程都要在此目录下创建临时的顺
序节点，因为Zk会为我们保证节点的顺序性，所以可以利用节点的顺序进行锁的判断。
   2).每个线程都是先创建临时顺序节点，然后获取当前目录下最小的节点(序号)，判断最小节点是不是
当前节点，如果是那么获取锁成功，如果不不是那么获取锁失败。
   3).获取锁失败的线程获取当前节点上一个临时顺序节点，并对对此节点进行监听，当该节点删除的时
候(上一个线程执行结束删除或者是掉线zk删除临时节点)这个线程会获取到通知，代表获取到了锁。
   代码实现
   main方法
package com.lagou.zk.dislock;
//zk实现分布式锁
public class DisLockTest {
    public static void main(String[] args) {
        //使用10个线程模拟分布式环境
        for (int i = 0;i<10;i++) {
            new Thread(new DisLockRunnable()).start();//启动线程
        }
    }

    static class DisLockRunnable implements Runnable{

        public void run() {
            //每个线程具体的任务，每个线程就是抢锁
            final DisClient client = new DisClient();
            client.getDisLock();

            //模拟获取锁之后的其他动作
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //释放锁
            client.deleteLock();

        }
    }
}

   核心实现
package com.lagou.zk.dislock;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

//抢锁
//1.去zk中创建临时序列节点，并获取序号
//2.判断自己创建节点序号是否是当前节点最小序号，如果是则获取锁
//执行相关操作，最后要释放锁
//3.不是最小节点，当前线程需要等待，等待你的前一个序号节点
//被删除，然后再次判断自己是否是最小节点
public class DisClient {

    public DisClient() {
        //初始化zk的/distrilock节点,会出现线程安全问题
        synchronized (DisClient.class) {
            if(!zkClient.exists("/distrilock")) {
                zkClient.createPersistent("/distrilock");
            }
        }
    }

    //前一个节点
    String beforNodePath;
    //当前线程的节点
    String currentNodePath;
    //获取到zkclient
   private ZkClient zkClient = new ZkClient("linux121:2181, linux122:2181");
   //把枪锁过程分为两部分，一部分是创建节点，比较序号，另一部分是等待锁

    //完整获取锁方法
    public void getDisLock() {
        //获取当前线程名称
        final String threadName = Thread.currentThread().getName();
        //首先调用tryGetLock
        if(tryGetLock()) {
            //说明获取到锁
            System.out.println(threadName+":获取到了锁");
        } else {
            //没有获取到锁
            System.out.println(threadName+":获取锁失败,进入等待状态");
            waitForLock();
            //递归获取锁
            getDisLock();
        }
    }
    CountDownLatch countDownLatch = null;
    //尝试获取锁
    public boolean tryGetLock(){
        //创建临时顺序节点，/distrilock/序号
        if (null == currentNodePath || "".equals(currentNodePath)){
            currentNodePath = zkClient.createEphemeralSequential("/distrilock/", "lock");
        }
        //获取到/distrilock下所有的子节点
        final List<String> childs = zkClient.getChildren("/distrilock");
        //对节点信息进行排序
        Collections.sort(childs);//默认是升序
        final String minNode = childs.get(0);
        //判断自己创建节点是否与最小序号一致
        if (currentNodePath.equals("/distrilock/"+minNode)) {
            //说明当前线程创建的就是序号最小节点
            return true;
        } else {
            //说明最小节点不是自己创建，要监控自己当前节点序号的前一个的节点
            final int i = Collections.binarySearch(childs, currentNodePath.substring("/distrilock/".length()));
            //前一个(lastNodeChild不包括父节点)
            String lastNodeChild = childs.get(i - 1);
            beforNodePath = "/distrilock/" + lastNodeChild;
        }
        return false;
    }
    //等待之前节点释放锁，如何判断锁被释放，需要唤醒线程继续尝试tryGetLock
    public void waitForLock()  {
        //准备一个监听器
        IZkDataListener iZkDataListener = new IZkDataListener() {
            public void handleDataChange(String s, Object o) throws Exception {

            }
              //删除
            public void handleDataDeleted(String s) throws Exception {
                 //提醒当前线程再次获取锁
                countDownLatch.countDown();//把值减1变为0，唤醒之前await线程
            }
        };
        //监控前一个节点
        zkClient.subscribeDataChanges(beforNodePath, iZkDataListener);

        //在监听的通知没来之前，该线程应该是等待状态，先判断一次上一个节点是否存在
        if (zkClient.exists(beforNodePath)) {
            //开始等待,CountDownLatch:线程同步计数器
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();//阻塞，知道countDownLatch变为0
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //解除监听
        zkClient.unsubscribeDataChanges(beforNodePath, iZkDataListener);
    }

    //释放锁
    public void deleteLock() {
        if (zkClient !=null) {
            zkClient.delete(currentNodePath);
            zkClient.close();
        }
    }
}

   分布式锁的实现可以是 Redis、Zookeeper，相对来说生产环境如果使用分布式锁可以考虑使用Redis实
现而非Zk。